package logcore

import (
	"fmt"
	"math"
	"mylogger/TLog"
	"mylogger/TLog/buffer"
	"sync"
	"time"
	"unicode/utf8"
)

const (
	defaultTimeLayout = time.RFC3339
)
const _hex = "0123456789abcdef"

var _standardPool = sync.Pool{New: func() interface{} {
	return &StandardFormatter{}
}}

func getStandard() *StandardFormatter {
	return _standardPool.Get().(*StandardFormatter)
}

func putStandardFormatter(sf *StandardFormatter) {
	//if sf.reflectBuf != nil {
	//	sf.reflectBuf.Free()
	//}
	//sf.EncoderConfig = nil
	sf.buf = nil
	sf.spaced = false
	//sf.openNamespaces = 0
	//sf.reflectBuf = nil
	//sf.reflectEnc = nil
	_standardPool.Put(sf)
}

type Format interface {
	Formatter(entry *Entry) (*buffer.Buffer, error)
}

type StandardFormatter struct {
	timeLayout string
	buf        *buffer.Buffer
	spaced     bool
	//flag int
}

func (sf *StandardFormatter) addElementSeparator() {
	last := sf.buf.Len() - 1
	if last < 0 {
		return
	}
	switch sf.buf.Bytes()[last] {
	case '{', '[', ':', ',', ' ':
		return
	default:
		sf.buf.AppendByte(' ')
		//if sf.spaced {
		//	sf.buf.AppendByte(' ')
		//}
	}
}

func (sf *StandardFormatter) AppendString(val string) {
	sf.addElementSeparator()
	sf.buf.AppendByte('"')
	sf.safeAddString(val)
	sf.buf.AppendByte('"')
}

func (sf *StandardFormatter) AppendComplex128(val complex128) {
	sf.addElementSeparator()
	// Cast to a platform-independent, fixed-size type.
	r, i := float64(real(val)), float64(imag(val))
	sf.buf.AppendByte('"')
	// Because we're always in a quoted string, we can use strconv without
	// special-casing NaN and +/-Inf.
	sf.buf.AppendFloat(r, 64)
	sf.buf.AppendByte('+')
	sf.buf.AppendFloat(i, 64)
	sf.buf.AppendByte('i')
	sf.buf.AppendByte('"')
}

func (sf *StandardFormatter) appendFloat(val float64, bitSize int) {
	sf.addElementSeparator()
	switch {
	case math.IsNaN(val):
		sf.buf.AppendString(`"NaN"`)
	case math.IsInf(val, 1):
		sf.buf.AppendString(`"+Inf"`)
	case math.IsInf(val, -1):
		sf.buf.AppendString(`"-Inf"`)
	default:
		sf.buf.AppendFloat(val, bitSize)
	}
}
func (sf *StandardFormatter) safeAddString(s string) {
	for i := 0; i < len(s); {
		if sf.tryAddRuneSelf(s[i]) {
			i++
			continue
		}
		r, size := utf8.DecodeRuneInString(s[i:])
		if sf.tryAddRuneError(r, size) {
			i++
			continue
		}
		sf.buf.AppendString(s[i : i+size])
		i += size
	}
}

// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte.
func (sf *StandardFormatter) safeAddByteString(s []byte) {
	for i := 0; i < len(s); {
		if sf.tryAddRuneSelf(s[i]) {
			i++
			continue
		}
		r, size := utf8.DecodeRune(s[i:])
		if sf.tryAddRuneError(r, size) {
			i++
			continue
		}
		sf.buf.Write(s[i : i+size])
		i += size
	}
}

func (sf *StandardFormatter) AppendInt64(val int64) {
	sf.addElementSeparator()
	sf.buf.AppendInt(val)
}
func (sf *StandardFormatter) AppendUint64(val uint64) {
	sf.addElementSeparator()
	sf.buf.AppendUint(val)
}

//func (sf *StandardFormatter) AddComplex64(k string, v complex64) { sf.AddComplex128(k, complex128(v)) }
//func (sf *StandardFormatter) AddFloat32(k string, v float32)     { sf.AddFloat64(k, float64(v)) }
//func (sf *StandardFormatter) AddInt(k string, v int)             { sf.AddInt64(k, int64(v)) }
//func (sf *StandardFormatter) AddInt32(k string, v int32)         { sf.AddInt64(k, int64(v)) }
//func (sf *StandardFormatter) AddInt16(k string, v int16)         { sf.AddInt64(k, int64(v)) }
//func (sf *StandardFormatter) AddInt8(k string, v int8)           { sf.AddInt64(k, int64(v)) }
//func (sf *StandardFormatter) AddUint(k string, v uint)           { sf.AddUint64(k, uint64(v)) }
//func (sf *StandardFormatter) AddUint32(k string, v uint32)       { sf.AddUint64(k, uint64(v)) }
//func (sf *StandardFormatter) AddUint16(k string, v uint16)       { sf.AddUint64(k, uint64(v)) }
//func (sf *StandardFormatter) AddUint8(k string, v uint8)         { sf.AddUint64(k, uint64(v)) }
//func (sf *StandardFormatter) AddUintptr(k string, v uintptr)     { sf.AddUint64(k, uint64(v)) }
func (sf *StandardFormatter) AppendComplex64(v complex64) { sf.AppendComplex128(complex128(v)) }
func (sf *StandardFormatter) AppendFloat64(v float64)     { sf.appendFloat(v, 64) }
func (sf *StandardFormatter) AppendFloat32(v float32)     { sf.appendFloat(float64(v), 32) }
func (sf *StandardFormatter) AppendInt(v int)             { sf.AppendInt64(int64(v)) }
func (sf *StandardFormatter) AppendInt32(v int32)         { sf.AppendInt64(int64(v)) }
func (sf *StandardFormatter) AppendInt16(v int16)         { sf.AppendInt64(int64(v)) }
func (sf *StandardFormatter) AppendInt8(v int8)           { sf.AppendInt64(int64(v)) }
func (sf *StandardFormatter) AppendUint(v uint)           { sf.AppendUint64(uint64(v)) }
func (sf *StandardFormatter) AppendUint32(v uint32)       { sf.AppendUint64(uint64(v)) }
func (sf *StandardFormatter) AppendUint16(v uint16)       { sf.AppendUint64(uint64(v)) }
func (sf *StandardFormatter) AppendUint8(v uint8)         { sf.AppendUint64(uint64(v)) }
func (sf *StandardFormatter) AppendUintptr(v uintptr)     { sf.AppendUint64(uint64(v)) }

func (sf *StandardFormatter) AppendTimeLayout(time time.Time, layout string) {
	sf.addElementSeparator()
	sf.buf.AppendByte('"')
	sf.buf.AppendTime(time, layout)
	sf.buf.AppendByte('"')
}

func (sf *StandardFormatter) AppendTime(t time.Time) {
	nanos := t.UnixNano()
	millis := float64(nanos) / float64(time.Millisecond)
	sf.AppendFloat64(millis)
}

func (sf *StandardFormatter) Formatter(entry *Entry) (*buffer.Buffer, error) {
	var b *buffer.Buffer
	if entry.Buffer != nil {
		b = entry.Buffer
	} else {
		b = &buffer.Buffer{}
	}
	if entry.Logger.Flag&TLog.PrefixHead != 0 {
		b.AppendByte('[')
		sf.safeAddString(entry.LevelName)
		b.AppendByte(']')
		entry.Logger.Flag = entry.Logger.Flag & (^(TLog.PrefixMid | TLog.PrefixMsg))

	}
	sf.AppendTime(entry.Time)

	if entry.Logger.Flag&TLog.PrefixMsg != 0 {
		b.WriteString(entry.LevelName)
		entry.Logger.Flag = entry.Logger.Flag & (^TLog.PrefixMsg)
		b.WriteByte(' ')
	}
	if entry.Name != "" {
		b.WriteString(entry.Name)
		b.WriteByte(' ')
	}
	if entry.HasCaller() {
		if entry.Logger.Flag&TLog.Lshortfile != 0 {
			short := entry.Caller.File
			for i := len(entry.Caller.File) - 1; i > 0; i-- {
				if entry.Caller.File[i] == '/' {
					short = entry.Caller.File[i+1:]
					break
				}
			}
			entry.Caller.File = short
		}
		b.WriteString(fmt.Sprintf("%s:%d:%s() ", entry.Caller.File, entry.Caller.Line, entry.Caller.Function))
	}
	if entry.Logger.Sing != "" {
		b.WriteString(entry.Logger.Sing)
		b.WriteByte(' ')
	}

	if entry.Logger.Flag&TLog.PrefixMsg != 0 {
		b.WriteString(entry.LevelName)
		b.WriteByte(' ')
	}

	b.WriteString(entry.Message)
	b.WriteByte(' ')
	b.WriteByte('\n')

	return b, nil
}

func (sf *StandardFormatter) tryAddRuneSelf(b byte) bool {
	if b >= utf8.RuneSelf {
		return false
	}
	if 0x20 <= b && b != '\\' && b != '"' {
		sf.buf.AppendByte(b)
		return true
	}
	switch b {
	case '\\', '"':
		sf.buf.AppendByte('\\')
		sf.buf.AppendByte(b)
	case '\n':
		sf.buf.AppendByte('\\')
		sf.buf.AppendByte('n')
	case '\r':
		sf.buf.AppendByte('\\')
		sf.buf.AppendByte('r')
	case '\t':
		sf.buf.AppendByte('\\')
		sf.buf.AppendByte('t')
	default:
		// Encode bytes < 0x20, except for the escape sequences above.
		sf.buf.AppendString(`\u00`)
		sf.buf.AppendByte(_hex[b>>4])
		sf.buf.AppendByte(_hex[b&0xF])
	}
	return true
}

func (sf *StandardFormatter) tryAddRuneError(r rune, size int) bool {
	if r == utf8.RuneError && size == 1 {
		sf.buf.AppendString(`\ufffd`)
		return true
	}
	return false
}
